﻿using System;
using System.Collections;
using System.Collections.Generic;
using Gameplay.PVE;
using UnityEditor;
using UnityEngine;
using UnityEngine.Profiling;
using Random = UnityEngine.Random;

public class RpgAstarEditorManager : MonoBehaviour
{
    public static RpgAstarEditorManager Instance;
    private AStarMgr astarMgr;
    private SmoothModifier smoothModifier;
    
    [SerializeField,HideInInspector]
    public List<bool> grids;
    public int width;
    public int height;

    private bool hasMapChanged = false;
    public bool isAStarTesting = false;
    
    public enum EAStarEditorMode
    {
        None = 0,
        EditMap = 1,
        FindPath = 2,
        PerformanceTest = 3,
    };

    public EAStarEditorMode Mode;
    //0 无 1 编辑地图模式 2 寻路模式
    // Start is called before the first frame update
    void Start()
    {
        Instance = this;
        astarMgr = new AStarMgr();
        smoothModifier = new SmoothModifier();
        InitMap(width, height);
        Camera.main.orthographicSize = (height + 1) * AStarMgr.gridSize.y / 2;
    }

    // Update is called once per frame
    void Update()
    {
        if (Mode == EAStarEditorMode.EditMap)
        {
            if (Input.GetMouseButtonUp(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray, out RaycastHit hitInfo))
                {
                    var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                    astarMgr.UpdateBlock(info.x,info.y,info.id,!info.isBlock);
                    info.isBlock = !info.isBlock;
                    var index = GetIndexByXY(info.x, info.y);
                    grids[index] = info.isBlock;
                    hasMapChanged = true;
                }
                    
            }
        }

        if (Mode == EAStarEditorMode.FindPath)
        {
            if (Input.GetMouseButtonUp(0))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray, out RaycastHit hitInfo))
                {
                    var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                    startPosition = hitInfo.transform.position;
                    FindPath();
                }
                    
            }
            else if (Input.GetMouseButtonUp(1))
            {
                Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                if (Physics.Raycast(ray, out RaycastHit hitInfo))
                {
                    var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                    //astarMgr.UpdateBlock(info.x,info.y,info.id,!info.isBlock);
                    endPosition = hitInfo.transform.position;
                    FindPath();
                }
                    
            }
        }

        if (Mode == EAStarEditorMode.PerformanceTest)
        {
            if (editRegionState == 0)
            {
                if (Input.GetMouseButtonDown(0))
                {
                    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                    if (Physics.Raycast(ray, out RaycastHit hitInfo))
                    {
                        var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                        findPathStartRegionCorner1 = new Vector2Int(info.x,info.y);
                    }
                    editRegionState = 1;
                }
                else if(Input.GetMouseButtonDown(1))
                {
                    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                    if (Physics.Raycast(ray, out RaycastHit hitInfo))
                    {
                        var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                        findPathEndRegionCorner1 = new Vector2Int(info.x,info.y);
                    }
                    editRegionState = 1;
                }
            }
            else if (editRegionState == 1)
            {
                if (Input.GetMouseButton(0))
                {
                    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                    if (Physics.Raycast(ray, out RaycastHit hitInfo))
                    {
                        var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                        findPathStartRegionCorner2 = new Vector2Int(info.x,info.y);
                    }
                    DrawRegion();
                    
                }
                else if(Input.GetMouseButton(1))
                {
                    Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
                    if (Physics.Raycast(ray, out RaycastHit hitInfo))
                    {
                        var info = hitInfo.transform.gameObject.GetComponent<AStarNodeInfo>();
                        findPathEndRegionCorner2 = new Vector2Int(info.x,info.y);
                    }
                    DrawRegion();
                }
                else if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1))
                {
                    editRegionState = 0;
                }
            }
        }
        
        astarMgr?.DrawGrids();
        if (isAStarTesting)
        {
            UpdateTask();
        }
    }

    private int editRegionState = 0;

    public int taskCount = 10;
    public void StartPerformanceTest()
    {
        isAStarTesting = true;
    }

    private List<Vector3> path = new List<Vector3>(100);
    
    private void UpdateTask()
    {
        for (int i = 0; i < taskCount; i++)
        {
            var startPositionX = Random.Range(startRegionMinX, startRegionMaxX);
            var startPositionY = Random.Range(startRegionMinY, startRegionMaxY);
            var startPosition = new Vector3(startPositionX, 0, startPositionY);
            
            var endPositionX = Random.Range(endRegionMinX, endRegionMaxX);
            var endPositionY = Random.Range(endRegionMinY, endRegionMaxY);
            var endPosition = new Vector3(endPositionX, 0, endPositionY);

            astarMgr.FindPath(ref path, 1, startPosition, endPosition);
            Profiler.BeginSample("Smooth");
            path = smoothModifier.Smooth(path);
            Profiler.EndSample();
        }
    }
    
    private Vector2Int findPathStartRegionCorner1;
    private Vector2Int findPathStartRegionCorner2;
    private int startRegionCornerId = 0;
    private GameObject startRegion;

    private float startRegionMinX;
    private float startRegionMaxX;
    private float startRegionMinY;
    private float startRegionMaxY;

    private Vector2Int findPathEndRegionCorner1;
    private Vector2Int findPathEndRegionCorner2;
    private int endRegionCornerId = 0;
    private GameObject endRegion;
    
    private Vector3 startPosition;
    private Vector3 endPosition;
    
    private float endRegionMinX;
    private float endRegionMaxX;
    private float endRegionMinY;
    private float endRegionMaxY;
    
    private void DrawRegion()
    {
        if (startRegion == null)
        {
            startRegion = GameObject.CreatePrimitive(PrimitiveType.Cube);
            startRegion.GetComponent<BoxCollider>().enabled = false;
        }

        if (endRegion == null)
        {
            endRegion = GameObject.CreatePrimitive(PrimitiveType.Cube);
            endRegion.GetComponent<BoxCollider>().enabled = false;
        }

        float width = Mathf.Abs(findPathStartRegionCorner1.x - findPathStartRegionCorner2.x);
        float height = Mathf.Abs(findPathStartRegionCorner1.y - findPathStartRegionCorner2.y);
        var center = new Vector3((findPathStartRegionCorner1.x + findPathStartRegionCorner2.x) / 2f * AStarMgr.gridSize.x,0,(findPathStartRegionCorner1.y + findPathStartRegionCorner2.y) / 2f * AStarMgr.gridSize.y);
        center = center - new Vector3(this.width * AStarMgr.gridSize.x / 2,0,this.height * AStarMgr.gridSize.y / 2);
        startRegion.transform.position = center;
        width = Mathf.Max(width, 1) * AStarMgr.gridSize.x;
        height = Mathf.Max(height, 1) * AStarMgr.gridSize.y;
        startRegion.transform.localScale = new Vector3(width,1,height);
        startRegionMinX = center.x - width / 2;
        startRegionMaxX = center.x + width / 2;
        startRegionMinY = center.z - height / 2;
        startRegionMaxY = center.z + height / 2;

        width = Mathf.Abs(findPathEndRegionCorner1.x - findPathEndRegionCorner2.x);
        height = Mathf.Abs(findPathEndRegionCorner1.y - findPathEndRegionCorner2.y);
        center = new Vector3((findPathEndRegionCorner1.x + findPathEndRegionCorner2.x) / 2f * AStarMgr.gridSize.x,0,(findPathEndRegionCorner1.y + findPathEndRegionCorner2.y) / 2f * AStarMgr.gridSize.y);
        center = center - new Vector3(this.width * AStarMgr.gridSize.x / 2,0,this.height * AStarMgr.gridSize.y / 2);
        endRegion.transform.position = center;
        width = Mathf.Max(width, 1) * AStarMgr.gridSize.x;
        height = Mathf.Max(height, 1) * AStarMgr.gridSize.y;
        endRegion.transform.localScale = new Vector3(width,1,height);
        endRegionMinX = center.x - width / 2;
        endRegionMaxX = center.x + width / 2;
        endRegionMinY = center.z - height / 2;
        endRegionMaxY = center.z + height / 2;

    }
    
    
    private List<GameObject> pathList = new List<GameObject>();

    private void FindPath()
    {
        for (int i = 0; i < pathList.Count; i++)
        {
            GameObject.DestroyImmediate(pathList[i]);
        }
        pathList.Clear();
        List<Vector3> path = new List<Vector3>();
        if (startPosition != Vector3.zero && endPosition != Vector3.zero)
        {
            var isSuccess = astarMgr.FindPath(ref path,-1, startPosition, endPosition);
            var smoothList = smoothModifier.Smooth(path);
            path = smoothList;
            for (int i = 0; i < path.Count; i++)
            {
                var obj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                obj.transform.localScale = new Vector3(0.5f,0.5f,0.5f);
                obj.transform.position = path[i];
                obj.GetComponent<SphereCollider>().enabled = false;
                pathList.Add(obj);
            }
        }
    }
    
    public void InitMap(int width, int height)
    {
        astarMgr.InitMapInfo(width,height,Vector3.zero);
        if (grids == null)
        {
            grids = new List<bool>();
        }

        var totalIndex = width * height;
        var needAddCount = totalIndex - grids.Count;
        for (int i = 0; i < needAddCount; i++)
        {
            grids.Add(false);
        }
        for (int i = 0; i < width; i++)
        {
            for (int j = 0; j < height; j++)
            {
                astarMgr.UpdateBlock(i,j,0,grids[GetIndexByXY(i,j)]);
            }
        }
    }

    public int GetIndexByXY(int x,int y)
    {
        var index = y * width + x;
        return index;
    }

    public void UpdateGrid(int width,int height)
    {
        
    }

    private void OnApplicationQuit()
    {
        if (hasMapChanged)
        {
            PrefabUtility.SaveAsPrefabAsset(gameObject, "Assets/ResourcesAssets/Pve/Prefabs/Test/AStarEditorRoot.prefab");
        }
    }
}
